package com.study.ktandroid.coroutines_apply

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.study.ktandroid.databinding.ActivityCoroutineBinding
import kotlinx.coroutines.*

class StartCoroutineActivity02 : AppCompatActivity() {

    lateinit var binding: ActivityCoroutineBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCoroutineBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //startCoroutines()
        //startCoroutines2()
        //testSync()
        testAsync()
    }

    //--------------------------------为suspend函数添加上下文----------------------------------------
    // 单纯的suspend并没有切换线程的作用，只是标记这是一个耗时操作必须放到协程中执行
    // 可以使用withContext进行线程切换
    private suspend fun test() {
        withContext(Dispatchers.IO) {
            println("${Thread.currentThread()} test")
        }
    }

    fun startCoroutines2() {
        GlobalScope.launch(Dispatchers.Main) {
            println("${Thread.currentThread().name} start")
            test()
            println("${Thread.currentThread().name} end")
        }
    }

    // 输出start、end 等待1s输出 job
    // 说明主线程并没有阻塞，而阻塞的1s发生在其他线程，而且都是在main线程输出 表示内部自动进行了线程切换
    // 顺序编写保证了逻辑上的直观性，协程的自动线程切换又保证了代码的非阻塞性
    fun startCoroutines() {
        println("${Thread.currentThread().name} start")
        GlobalScope.launch(Dispatchers.Main) {
            // suspend修饰的函数，是一个挂起函数，必须在协程或者其他suspend函数中执行
            // 他挂起的是协程而不是线程
            delay(1000L)
            println("${Thread.currentThread().name} job...")
        }
        println("${Thread.currentThread().name} end")
    }

    //-------------------------------协程简化更为复杂的业务逻辑-----------------------------------------
    // 如果有A、B、C三个请求，BC需要依赖A进行并发请求，最后更新UI
    fun testBiz() {
        GlobalScope.launch(Dispatchers.Main) {
            // 获取A的结果
            val p = t1()
            // BC进行异步请求
            val v1 = async { t2(p) }
            val v2 = async { t3(p) }
            updateUI(v1.await(), v2.await())
        }
    }

    suspend fun t1(): String {
        return "slx"
    }

    suspend fun t2(p: String): String {
        return p + "t2"
    }

    suspend fun t3(p: String): String {
        return p + "t3"
    }

    fun updateUI(p: String, p2: String) {
        binding.callbackTv.text = "$p $p2"
    }

    //------------------------------------------------------------------------

    // 如果一个页面需要请求三个接口，然后把他们共同的数据填充到UI
    // 1.借助RxJava
    // 2.使用协程 async await方法，其中耗时操作在子线程中，更新UI的操作自动切换到主线程
    // 使用async进行并发请求，使用await获取请求结果
    fun testAsync() {
        GlobalScope.launch(Dispatchers.Main) {
            val v1 = async { test1() }
            val v2 = async { test2("t2") }
            val v3 = async { test3("t3") }
            updateUI2(v1.await(), v2.await(), v3.await())
        }
    }

    // 去除回调，使用协程改造为串行
    fun testSync() {
        GlobalScope.launch(Dispatchers.Main) {
            val v1 = test1()
            val v2 = test2(v1)
            val v3 = test3(v2)
            updateUI(v3)
        }
    }

    suspend fun test1(): String {
        return "test1"
    }

    suspend fun test2(p: String): String {
        return "$p test2"
    }

    suspend fun test3(p: String): String {
        return "$p test3"
    }

    fun updateUI(params: String) {
        binding.callbackTv.text = params
    }

    fun updateUI2(p1: String, p2: String, p3: String) {
        binding.callbackTv.text = "$p1 $p2 $p3"
    }
}